import Cesium from '../Ces/Cesium'

class WeUniform {
  name: string
  _floats
  _vec2s
  _vec3s
  _vec4s
  _floatUniforms
  _vec2Uniforms
  _vec3Uniforms
  _vec4Uniforms
  _uniformProperty
  constructor(options) {
    const floatUniform = options.floatUniform ?? {}

    this.name = options.name?? 'u_WeUniform'
    this._floats = []
    this._vec2s = []
    this._vec3s = []
    this._vec4s = []
    this._floatUniforms = {}
    this._vec2Uniforms = {}
    this._vec3Uniforms = {}
    this._vec4Uniforms = {}

    const that = this

    const keys = Object.keys(floatUniform)

    for (let i = 0; i < keys.length; i++) {
      const uname = keys[i]

      this._floats[i] = floatUniform[uname]
      this._floatUniforms[uname] = {
        get() {
          return that._floats[i]
        },
        set(value) {
          that._floats[i] = value
        }
      }
    }

    while (this._floats.length % 3 === 0 || this._floats.length % 4 === 0)
      this._floats.push(0)

    const vec4Uniform = options.vec4Uniform

    if (vec4Uniform) {
      const keys = Object.keys(vec4Uniform)

      for (let i = 0; i < keys.length; i++) {
        const uname = keys[i]
        const value = vec4Uniform[uname]
        if (value instanceof Cesium.Rectangle) {
          const { west, south, east, north } = value
          this._vec4s[i] = new Cesium.Cartesian4(west, south, east, north)
        } else if (value instanceof Cesium.Color) {
          const { red, blue, green, alpha } = value
          this._vec4s[i] = new Cesium.Cartesian4(red, green, blue, alpha)
        } else {
          this._vec4s[i] = value
        }
        this._vec4Uniforms[uname] = {
          get() {
            return that._vec4s[i]
          },
          set(value) {
            if (value instanceof Cesium.Rectangle) {
              that._vec4s[i].x = value.west
              that._vec4s[i].y = value.south
              that._vec4s[i].z = value.east
              that._vec4s[i].w = value.north
            } else if (value instanceof Cesium.Cartesian4) {
              that._vec4s[i].x = value.x
              that._vec4s[i].y = value.y
              that._vec4s[i].z = value.z
              that._vec4s[i].w = value.w
            } else if (value instanceof Cesium.Color) {
              that._vec4s[i].x = value.red
              that._vec4s[i].y = value.green
              that._vec4s[i].z = value.blue
              that._vec4s[i].w = value.alpha
            } else {
              that._vec4s[i].x = value[0]
              that._vec4s[i].y = value[1]
              that._vec4s[i].z = value[2]
              that._vec4s[i].w = value[3]
            }
          }
        }
      }
    }

    const vec3Uniform = options.vec3Uniform

    if (vec3Uniform) {
      const keys = Object.keys(vec3Uniform)

      for (let i = 0; i < keys.length; i++) {
        const uname = keys[i]
        const value = vec3Uniform[uname]
        this._vec3s[i] = value
        this._vec3Uniforms[uname] = {
          get() {
            return that._vec3s[i]
          },
          set(value) {
            if (value instanceof Cesium.Cartesian3) {
              that._vec3s[i].x = value.x
              that._vec3s[i].y = value.y
              that._vec3s[i].z = value.z
            } else {
              that._vec3s[i].x = value[0]
              that._vec3s[i].y = value[1]
              that._vec3s[i].z = value[2]
            }
          }
        }
      }
    }

    const vec2Uniform = options.vec2Uniform

    if (vec2Uniform) {
      const keys = Object.keys(vec2Uniform)

      for (let i = 0; i < keys.length; i++) {
        const uname = keys[i]
        const value = vec2Uniform[uname]
        this._vec2s[i] = value
        this._vec2Uniforms[uname] = {
          get() {
            return that._vec2s[i]
          },
          set(value) {
            if (value instanceof Cesium.Cartesian2) {
              that._vec2s[i].x = value.x
              that._vec2s[i].y = value.y
            } else {
              that._vec2s[i].x = value[0]
              that._vec2s[i].y = value[1]
            }
          }
        }
      }
    }

    function UniformProperty() { }
    Object.defineProperties(UniformProperty.prototype, {
      ...this._floatUniforms,
      ...this._vec2Uniforms,
      ...this._vec3Uniforms,
      ...this._vec4Uniforms,
    })
    this._uniformProperty = new UniformProperty()
  }

  get u_WeUniform() {
    return this._floats
  }
  get u_WeUniformVec2() {
    return this._vec2s
  }
  get u_WeUniformVec3() {
    return this._vec3s
  }
  get u_WeUniformVec4() {
    return this._vec4s
  }
  get Properties() {
    return this._uniformProperty
  }

  setupUniform(uniforms) {
    const uniformName = this.name
    uniforms[uniformName] = this.u_WeUniform
    if (this.u_WeUniformVec2.length > 0) {
      uniforms[`${uniformName}_vec2`] = this.u_WeUniformVec2
    }
    if (this.u_WeUniformVec3.length > 0) {
      uniforms[`${uniformName}_vec3`] = this.u_WeUniformVec3
    }
    if (this.u_WeUniformVec4.length > 0) {
      uniforms[`${uniformName}_vec4`] = this.u_WeUniformVec4
    }
  }

  setupUniformMap(uniformMap) {
    const uniformName = this.name
    uniformMap[uniformName] = () => this.u_WeUniform
    if (this.u_WeUniformVec2.length > 0) {
      uniformMap[`${uniformName}_vec2`] = () => this.u_WeUniformVec2
    }
    if (this.u_WeUniformVec3.length > 0) {
      uniformMap[`${uniformName}_vec3`] = () => this.u_WeUniformVec3
    }
    if (this.u_WeUniformVec4.length > 0) {
      uniformMap[`${uniformName}_vec4`] = () => this.u_WeUniformVec4
    }
  }

  getSource(hasDeclare = true) {
    const keys = Object.keys(this._floatUniforms)
    const uniformName = this.name

    let source = ''

    let floatLength = this._floats.length
    const vec2Length = this._vec2s.length
    const vec3Length = this._vec3s.length
    const vec4Length = this._vec4s.length

    if (hasDeclare) {
      source += `uniform float ${uniformName}[${floatLength}];\n`
      if (vec2Length > 0) {
        source += `uniform vec2 ${uniformName}_vec2[${vec2Length}];\n`
      }
      if (vec3Length > 0) {
        source += `uniform vec3 ${uniformName}_vec3[${vec3Length}];\n`
      }
      if (vec4Length > 0) {
        source += `uniform vec4 ${uniformName}_vec4[${vec4Length}];\n`
      }
    }

    for (let i = 0; i < keys.length; i++) {
      const uname = keys[i]
      const func = `
      float Weget_${uname}()
      {
          return ${uniformName}[${i}];
      }
      `

      source += String(func)
    }

    if (vec2Length > 0) {
      const keys = Object.keys(this._vec2Uniforms)
      for (let i = 0; i < vec2Length; i++) {
        const uname = keys[i]
        source += `
      vec2 Weget_${uname}()
      {
        return ${uniformName}_vec2[${i}];
      }
      `
      }
    }

    if (vec3Length > 0) {
      const keys = Object.keys(this._vec3Uniforms)
      for (let i = 0; i < vec3Length; i++) {
        const uname = keys[i]
        source += `
      vec3 Weget_${uname}()
      {
        return ${uniformName}_vec3[${i}];
      }
      `
      }
    }

    if (vec4Length > 0) {
      const keys = Object.keys(this._vec4Uniforms)
      for (let i = 0; i < vec4Length; i++) {
        const uname = keys[i]
        source += `
      vec4 Weget_${uname}()
      {
        return ${uniformName}_vec4[${i}];
      }
      `
      }
    }
    return source
  }
}

export default WeUniform
